package collections

import (
	"fmt"
	"github.com/pkg/errors"
	"reflect"
	"strings"
)

var (
	ErrEmpty = errors.New("no item founded")
)

type FilterFunc func(item interface{}) bool

func Filter(vectors interface{}, filterFunc FilterFunc) interface{} {
	v := reflect.ValueOf(vectors)
	if v.Kind() != reflect.Slice && v.Kind() != reflect.Array {
		panic(fmt.Sprintf("vectors must be a array of slice your type is %s", v.String()))
	}
	var results []interface{}
	l := v.Len()
	for i := 0; i < l; i++ {
		item := v.Index(i).Interface()
		if filterFunc(item) {
			results = append(results, item)
		}
	}
	return results
}

func CollectionFilter(vectors interface{}, filterFunc FilterFunc, results interface{}) {
	v := reflect.ValueOf(vectors)
	if v.Kind() != reflect.Slice && v.Kind() != reflect.Array {
		panic(fmt.Sprintf("vectors must be a array or slice,but your type is %s", v.String()))
	}
	resultsValue := reflect.ValueOf(results)
	if resultsValue.Kind() != reflect.Ptr {
		panic("results's type must be a pointer")
	}
	el := resultsValue.Elem()
	newEl := el
	vlen := v.Len()
	for i := 0; i < vlen; i++ {
		if filterFunc(v.Index(i).Interface()) {
			newEl = reflect.Append(newEl, v.Index(i))
		}
	}
	el.Set(newEl)
	return
}

func Join(vectors interface{}, sep string, prefix ...string) string {
	v := reflect.ValueOf(vectors)
	if v.Kind() != reflect.Slice && v.Kind() != reflect.Array {
		panic(fmt.Sprintf("vectors must be a array of slice your type is %s", v.String()))
	}
	prefixLen := len(prefix)
	var prefixStr string
	var defaultValue = ""
	if prefixLen > 0 {
		prefixStr = prefix[0] + "."
		if prefixLen > 1 {
			defaultValue = prefix[1]
		}
	}
	l := v.Len()
	if l == 0 {
		return defaultValue
	}
	var buffer []string
	for i := 0; i < l; i++ {
		el := v.Index(i).Interface()
		switch elV := el.(type) {
		case int, uint, int32, int16, int64:
			buffer = append(buffer, fmt.Sprintf("%s%d", prefixStr, elV))
			break
		case string:
			buffer = append(buffer, fmt.Sprintf("%s%s", prefixStr, elV))
			break
		default:
			buffer = append(buffer, prefixStr+fmt.Sprint(elV))
		}
	}
	return strings.Join(buffer, sep)
}

func CollectionMap(value interface{}, filter func(item interface{}) interface{}, results interface{}) {
	v := reflect.ValueOf(value)
	if v.Kind() != reflect.Slice {
		panic("value's type must be a slice")
	}
	vlen := v.Len()
	resultsValue := reflect.ValueOf(results)
	if resultsValue.Kind() != reflect.Ptr {
		panic("results's type must be a pointer")
	}
	el := resultsValue.Elem()
	newEl := el
	for i := 0; i < vlen; i++ {
		newEl = reflect.Append(newEl, reflect.ValueOf(filter(v.Index(i).Interface())))
	}
	el.Set(newEl)
	return
}

func CollectionSome(vectors interface{}, filterFunc FilterFunc) (rt bool) {
	v := reflect.ValueOf(vectors)
	v = reflect.Indirect(v)
	if v.Kind() != reflect.Slice && v.Kind() != reflect.Array {
		panic(fmt.Sprintf("vectors must be a array or slice, but your type is %s", v.String()))
	}
	vlen := v.Len()
	for i := 0; i < vlen; i++ {
		if filterFunc(v.Index(i).Interface()) {
			rt = true
			return
		}
	}
	return
}

func CollectionOne(vectors interface{}, filterFunc FilterFunc, results interface{}) (err error) {
	v := reflect.ValueOf(vectors)
	if v.Kind() != reflect.Slice && v.Kind() != reflect.Array {
		panic(fmt.Sprintf("vectors must be a array or slice,but your type is %s", v.String()))
	}
	resultsValue := reflect.ValueOf(results)
	if resultsValue.Kind() != reflect.Ptr {
		panic("results's type must be a pointer")
	}
	vlen := v.Len()
	for i := 0; i < vlen; i++ {
		if filterFunc(v.Index(i).Interface()) {
			resultsValue.Elem().Set(v.Index(i))
			return
		}
	}
	err = ErrEmpty
	return
}
